package uva.sc.ql.typeChecker;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import uva.sc.core.INode;
import uva.sc.core.errors.Arithmetic;
import uva.sc.core.errors.CyclicDependency;
import uva.sc.core.errors.DuplicatedID;
import uva.sc.core.errors.IError;
import uva.sc.core.errors.TypeMissmatch;
import uva.sc.core.errors.UndefinedID;
import uva.sc.core.types.Boolean;
import uva.sc.core.types.Number;
import uva.sc.core.types.Type;
import uva.sc.core.types.Undefined;
import uva.sc.core.warnings.DuplicatedLabel;
import uva.sc.core.warnings.IWarning;
import uva.sc.ql.ast.IQLExpressionNodeVisitor;
import uva.sc.ql.ast.IQLFormNodeVisitor;
import uva.sc.ql.ast.IQLStatementNodeVisitor;
import uva.sc.ql.atom.BooleanAtom;
import uva.sc.ql.atom.ID;
import uva.sc.ql.atom.NumberAtom;
import uva.sc.ql.atom.StringAtom;
import uva.sc.ql.expression.Expression;
import uva.sc.ql.expression.binaryExpressions.Addition;
import uva.sc.ql.expression.binaryExpressions.And;
import uva.sc.ql.expression.binaryExpressions.BinaryExpression;
import uva.sc.ql.expression.binaryExpressions.Division;
import uva.sc.ql.expression.binaryExpressions.Equals;
import uva.sc.ql.expression.binaryExpressions.GreaterThan;
import uva.sc.ql.expression.binaryExpressions.GreaterThanEquals;
import uva.sc.ql.expression.binaryExpressions.LesserThan;
import uva.sc.ql.expression.binaryExpressions.LesserThanEquals;
import uva.sc.ql.expression.binaryExpressions.Modulus;
import uva.sc.ql.expression.binaryExpressions.Multiplication;
import uva.sc.ql.expression.binaryExpressions.NotEquals;
import uva.sc.ql.expression.binaryExpressions.Or;
import uva.sc.ql.expression.binaryExpressions.Substraction;
import uva.sc.ql.expression.unaryExpressions.Minus;
import uva.sc.ql.expression.unaryExpressions.Not;
import uva.sc.ql.form.Form;
import uva.sc.ql.statements.IfStatement;
import uva.sc.ql.statements.Question;
import uva.sc.ql.statements.Statement;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class TypeCheckerVisitor implements IQLFormNodeVisitor<INode>,
IQLStatementNodeVisitor<INode>, IQLExpressionNodeVisitor<INode> {
private List<IError> errors;
private List<IWarning> warnings;
private List<String> questionLabels;
private ID formTitle;
private ID currentQuestion;
private Map<ID, Type> symbolTable = new HashMap<ID, Type>();
// getters
public List<IError> getErrors() {
return errors;
}
public List<IWarning> getWarnings() {
return warnings;
}
public Map<ID, Type> getSymbolTable() {
return symbolTable;
}
public ID getFormTitle() {
return formTitle;
}
// constructor
public TypeCheckerVisitor() {
questionLabels = new ArrayList<String>();
errors = new ArrayList<IError>();
warnings = new ArrayList<IWarning>();
}
// visit methods
public Form visit(Form questionnaire) {
formTitle = questionnaire.getId();
symbolTable.put(formTitle, null);
List<Statement> statements = questionnaire.getStatements();
for (Statement statement : statements) {
statement.accept(this);
}
return null;
}
public Question visit(Question question) {
currentQuestion = question.getId();
String questionLabel = question.getStr();
Type type = question.getType();
checkDuplicatedQuestionIDs(type);
checkDuplicatedQuestionLabels(questionLabel);
Expression expr = question.getExpr();
if (expr != null) {
expr.accept(this);
}
currentQuestion = null;
return null;
}
public Type visit(ID id) {
Type result = new Undefined();
if (!symbolTable.containsKey(id)) {
errors.add(new UndefinedID(id));
} else {
result = symbolTable.get(id);
if (id.equals(currentQuestion)) {
errors.add(new CyclicDependency(id));
}
}
return result;
}
public IfStatement visit(IfStatement ifStatement) {
Expression expr = ifStatement.getExpr();
Type type = (Type) expr.accept(this);
if (!type.equals(new Boolean())) {
errors.add(new TypeMissmatch(type, new Boolean()));
}
List<Question> questions = ifStatement.getQuestions();
for (Question question : questions)
question.accept(this);
return null;
}
/* ---------------------- Arithmetic Type Checking ------------------------ */
public Type arithmeticTypeCheck(BinaryExpression arithmetic) {
Expression expr1 = (Expression) arithmetic.getFirstOperand();
Expression expr2 = (Expression) arithmetic.getSecondOperand();
Type firstOperandType = (Type) expr1.accept(this);
Type secondOperandType = (Type) expr2.accept(this);
if (!(firstOperandType.equals(new Number()) && secondOperandType
.equals(new Number()))) {
errors.add(new Arithmetic());
}
return new Number();
}
public Type visit(Addition addition) {
return arithmeticTypeCheck(addition);
}
public Type visit(Substraction sub) {
return arithmeticTypeCheck(sub);
}
public Type visit(Division division) {
return arithmeticTypeCheck(division);
}
public Type visit(GreaterThan greaterThan) {
return arithmeticTypeCheck(greaterThan);
}
public Type visit(GreaterThanEquals greaterThanEquals) {
return arithmeticTypeCheck(greaterThanEquals);
}
public Type visit(LesserThan lesserThan) {
return arithmeticTypeCheck(lesserThan);
}
public Type visit(LesserThanEquals lesserThanEquals) {
return arithmeticTypeCheck(lesserThanEquals);
}
public Type visit(Modulus mod) {
return arithmeticTypeCheck(mod);
}
public Type visit(Multiplication mult) {
return arithmeticTypeCheck(mult);
}
/* ======================================================================== */
/* ------------------------ Equality Type Checking ------------------------ */
private Type equalityTypeCheck(BinaryExpression equals) {
Expression expr1 = (Expression) equals.getFirstOperand();
Expression expr2 = (Expression) equals.getSecondOperand();
Type firstOperandType = (Type) expr1.accept(this);
Type secondOperandType = (Type) expr2.accept(this);
if (!firstOperandType.equals(secondOperandType)) {
errors.add(new TypeMissmatch(secondOperandType, firstOperandType));
}
return firstOperandType;
}
public Type visit(Equals equals) {
return equalityTypeCheck(equals);
}
public Type visit(NotEquals notEquals) {
return equalityTypeCheck(notEquals);
}
/* ======================================================================== */
/* ----------------------- Boolean Type Checking -------------------------- */
public Type booleanTypeCheck(BinaryExpression bool) {
Expression expr1 = bool.getFirstOperand();
Expression expr2 = bool.getSecondOperand();
Type firstOperandType = (Type) expr1.accept(this);
Type secondOperandType = (Type) expr2.accept(this);
if (!((new Boolean().equals(firstOperandType)) && (new Boolean()
.equals(secondOperandType)))) {
errors.add(new uva.sc.core.errors.Boolean());
}
return new Boolean();
}
public Type visit(And and) {
return booleanTypeCheck(and);
}
public Type visit(Or or) {
return booleanTypeCheck(or);
}
/* ======================================================================== */
/* ---------------------- Unary Type Checking ----------------------------- */
public Type visit(Minus minus) {
Expression expr = (Expression) minus.getOperand();
Type typeOperand = (Type) expr.accept(this);
if (!typeOperand.equals(new Boolean())) {
errors.add(new TypeMissmatch(typeOperand, new Number()));
}
return typeOperand;
}
public Type visit(Not not) {
Expression expr = (Expression) not.getOperand();
Type typeOperand = (Type) expr.accept(this);
if (typeOperand.equals(new Boolean())) {
errors.add(new TypeMissmatch(typeOperand, new Boolean()));
}
return typeOperand;
}
/* ======================================================================== */
public Type visit(BooleanAtom bool) {
return new Boolean();
}
public Type visit(NumberAtom doub) {
return new Number();
}
public Type visit(StringAtom str) {
return new uva.sc.core.types.String();
}
private void checkDuplicatedQuestionIDs(Type type) {
if (!symbolTable.containsKey(currentQuestion)) {
symbolTable.put(currentQuestion, type);
} else {
errors.add(new DuplicatedID(currentQuestion));
}
}
private void checkDuplicatedQuestionLabels(String questionLabel) {
if (questionLabels.contains(questionLabel)) {
warnings.add(new DuplicatedLabel(questionLabel));
} else {
questionLabels.add(questionLabel);
}
}
}